/*
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details:
 * http://www.gnu.org/copyleft/lesser.html#TOC3
 */
package com.simplemonkey;


import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;


import com.jme.scene.Node;
import com.simplemonkey.layout.LayoutManager;
import com.simplemonkey.layout.RowLayout;
import com.simplemonkey.util.Dimension;

/**
 * A Container is a set of Widgets. The
 * layout manager that is assigned to the Container is responsible for
 * the size and position of its content. In terms of the tree
 * data structure, a container is a node with an arbitrary number
 * of child nodes. 
 * 
 * @author Johannes Schaback
 * @dedicated NOFX - Lazy
 */
public class Container extends Widget implements IContainer
{
	private LayoutManager layoutManager = null;
	protected List<IWidget> notifyList = new CopyOnWriteArrayList<IWidget>();
	private boolean keyTraversalRoot = false;
	
	private Node node;

	/**
	 * Creates a new <code>Container</code>.
	 */
	public Container()
	{
		this(new RowLayout());
	}

	public Container(LayoutManager layoutManager)
	{
		this("Container",  new Node(),layoutManager);
	}

	public Container(Node node)
	{
		this("Container",  node,new RowLayout());
	}

	public Container(String name, Node node, LayoutManager layoutManager)
	{
		super("Container");
		this.node = node;
		this.layoutManager = layoutManager;
	}

	public boolean isKeyTraversalRoot()
	{
		return keyTraversalRoot;
	}

	public void setKeyTraversalRoot(boolean traversalRoot)
	{
		this.keyTraversalRoot = traversalRoot;
	}



	
	
	protected void resize() {
		this.layout();
		super.resize();
	}

	/**
	 * Returns true as containers are always traversable. Note that
	 * the focus gets forwarded to the first widget in the container.
	 */
	public boolean isTraversable()
	{
		return true;
	}

	/**
	 * Returns the children of this container.
	 * @return the children Widgets
	 */
	public java.util.List<IWidget> getContent()
	{
		return notifyList;
	}

	/**
	 * Adds a Widget to the container.
	 * @param c
	 *      The Widget to be added.
	 */
	public final void addWidget(IWidget c, int position)
	{
		if (c == null)
			return;

		if (c.equals(this))
		{
			throw new IllegalArgumentException("Can't add myself! c.equals(this)");
		}

		if (c.equals(getParent()))
		{
			throw new IllegalArgumentException("Can't add my parent!");
		}

		if (position < 0)
			position = 0;

		synchronized (notifyList)
		{
			if (position > notifyList.size())
				position = notifyList.size();

			if (notifyList.contains(c))
			{
				//System.err.println("Container.addWidget: Widget " + c + " is already in the container (" + this + ")");
			}
			else
			{
				//if(relyFocus() == null && c.relyFocus() != null) setRelyFocus(c);
				notifyList.add(position, c);

				c.setParent(this);
				this.getSpatial().attachChild(c.getSpatial());
				c.getSpatial().updateRenderState();

			}
		}
		updateMinSize();

	}

	/**
	 * Reorders the children such that the given child is drawn last and therefore appears as the top child.
	 * @param child the child to bring to top
	 */
	public void bringToFront(IWidget child)
	{
		synchronized (notifyList)
		{
			if (!notifyList.contains(child))
				throw new IllegalArgumentException("The given child must be in this container");

			notifyList.remove(child);
			notifyList.add(notifyList.size(), child);
		}
	}

	public void addWidget(IWidget widget)
	{
		addWidget(widget, notifyList.size());
	}


	/**
	 * Sets the layout manager.
	 * @param lm layout manager
	 */
	public void setLayoutManager(LayoutManager lm)
	{
		if (lm == null)
			return;
		layoutManager = lm;

		updateMinSize();
	}

	/**
	 * Returns the currently set layout manager.
	 * @return layout manager
	 */
	public LayoutManager getLayoutManager()
	{
		return layoutManager;
	}

	/**
	 * Layouts this Container according to his layout manager.
	 */
	
	public void layout()
	{
		//only use visible widgets
		List<IWidget> widgets = new ArrayList<IWidget>(notifyList.size());

		for (IWidget widget : notifyList)
		{
			if (widget.isVisible())
				widgets.add(widget);
		}

		// layout this container according to the min. sizes of the children
		// and my own size. Since i know the min. size of the children and
		// my final size, I can set the final size of my children as well.

		layoutManager.doLayout(this, widgets);

		synchronized (notifyList)
		{
			// pass layout call to my children.
			for (IWidget c : widgets)
				c.layout();
		}
		getSpatial().updateGeometricState(0, true);
	}

	/**
	 * Removes the given Widget from this Container
	 * @param c the Widget
	 */
	public void removeWidget(IWidget c)
	{
		if (c == null)
			return;
		if (c.equals(this))
			throw new IllegalArgumentException("Cannot remove myself! " + this);

		notifyList.remove(c);

		c.setParent(null);
		this.getSpatial().detachChild(c.getSpatial());

		updateMinSize();




	}

	/**
	 * Removes the specified
	 * direct child Widgets from this Container.
	 *   
	 * @param list list with Widgets to be removed
	 */
	public void removeWidgets(java.util.List<IWidget> list)
	{
		if (list == null)
			return;

		for (int i = 0; i < list.size(); i++)
		{
			removeWidget(list.get(i));
		}
	}

	/**
	 * Removes all Widgets from this Container
	 */
	public void removeAllWidgets()
	{
		while (size() > 0)
			removeWidget(getWidget(0));
	}

	public IWidget getInternalWidget(float x, float y)
	{
		if (!isVisible() || !getSpacingAppearance().insideMargin(x, y))
		{
			return null;
		}

		IWidget ret = null;
		IWidget hit = null;//don't return this widget anymore
		x -= getSpacingAppearance().getLeftMargins();
		y -= getSpacingAppearance().getBottomMargins();

		synchronized (notifyList)
		{
			//go from front (last item) to back (first item)
			for (int i = notifyList.size() - 1; i >= 0; i--)
			{
				IWidget w = notifyList.get(i);
				ret = w.getWidget(x - w.getX(), y - w.getY());

				if (ret != null)
				{
					hit = ret;
					break;
				}
			}
		}
		return hit;
	}
	

	/**
	 * Puts the name of the children in a String.
	 */
	
	public String toString()
	{
		if (notifyList == null)
		{
			return super.toString() + " {}";
		}

		String s = super.toString() + " {";

		synchronized (notifyList)
		{
			for (int i = 0; i < notifyList.size(); i++)
			{
				s += notifyList.get(i).getClass().getSimpleName();
				if (i < notifyList.size() - 1)
					s += ", ";
			}
		}
		s += "}";
		return s;
	}
	
	
	



	
	public Node getSpatial() {
		return node;
	}

	/**
	 * Returns the number of direct children.
	 * 
	 * @return number of children.
	 */
	public int size()
	{
		return notifyList.size();
	}

	/**
	 * Returns the child Widget with the specified index.
	 * @param index the index of the child Widget
	 * @return the child Widget
	 */
	public IWidget getWidget(int index)
	{
		return notifyList.get(index);
	}

	/**
	 * Returns all direct children.
	 * 
	 * @return children Widgets
	 */
	public Iterable<IWidget> getWidgets()
	{
		return notifyList;
	}

	/**
	 * Layouts and sets the container to the minimum
	 * size. Calling this method is equal to calling
	 * this sequence of commands.
	 * 
	 * <code>
	 * setSizeToMinSize();
	 * layout();
	 * </code>
	 * 
	 */
	public void pack()
	{
		setSizeToMinSize();
		layout();
	}

	/**
	 * Returns the predecessor of the given widget.
	 * @param currentWidget given widget, or null for last widget in container
	 * @return widget the previous widget
	 */
	public IWidget getPreviousWidget(IWidget currentWidget)
	{
		int i;
		synchronized (notifyList)
		{
			if (currentWidget == null)
				i = size() - 1;
			else
				i = notifyList.indexOf(currentWidget) - 1;

			if (i < 0)
			{
				if (isKeyTraversalRoot() && 0 != size() - 1)
					i = size() - 1;
				else
					return null;
				//				return getParent().getPreviousWidget(this);
			}

			return notifyList.get(i);
		}
	}

	/**
	 * Returns the successor of the given widget.
	 * @param currentWidget the given widget, or null to return first in container
	 * @return next widget
	 */
	public IWidget getNextWidget(IWidget currentWidget)
	{
		int i;
		synchronized (notifyList)
		{
			if (currentWidget == null)
				i = 0;
			else
				i = notifyList.indexOf(currentWidget) + 1;

			if (i > size() - 1)
			{
				if (isKeyTraversalRoot() && (size() - 1) != 0)
					i = 0;
				else
					return null;
			}

			return notifyList.get(i);
		}
	}

	/**
	 * Returns the next traversable widget.
	 * @param currentWidget the wiget to start searching from
	 * @return next traversable widget
	 */
	public IWidget getNextTraversableWidget(IWidget currentWidget)
	{
		if (currentWidget != null && !notifyList.contains(currentWidget))
			throw new IllegalArgumentException("currentWidget is not child of this container!");

		IWidget w = getNextWidget(currentWidget);

		//search in this and deeper levels
		while (w != null)
		{
			if (w.isTraversable())
			{
				if (w instanceof IContainer)
				{
					if (!((IContainer) w).isKeyTraversalRoot())
					{
						IWidget tmp = ((IContainer) w).getNextTraversableWidget(null);
						if (tmp != null)
						{
							w = tmp;
							break;
						}
					}
				}
				else
				{
					//found next here
					break;
				}
			}
			w = getNextWidget(w);
		}

		//no widgets here go one level up and search there
		if (w == null && this.getParent() != null && !isKeyTraversalRoot())
		{
			w = this.getParent().getNextTraversableWidget(this);
		}
		else if (w == null && isKeyTraversalRoot())
		{
			w = getNextTraversableWidget(null);
		}

		return w;
	}

	/**
	 * Returns the previous trabersable widget.
	 * @param currentWidget the wiget to start searching from
	 * @return previous traversable widget
	 */
	public IWidget getPreviousTraversableWidget(IWidget currentWidget)
	{
		if (currentWidget != null && !notifyList.contains(currentWidget))
			throw new IllegalArgumentException("currentWidget is not child of this container!");

		IWidget w = getPreviousWidget(currentWidget);

		//search in this and deeper levels
		while (w != null)
		{
			if (w.isTraversable())
			{
				if (w instanceof IContainer)
				{
					if (!((IContainer) w).isKeyTraversalRoot())
					{
						IWidget tmp = ((IContainer) w).getPreviousTraversableWidget(null);
						if (tmp != null)
						{
							w = tmp;
							break;
						}
					}
				}
				else
				{
					//found next here
					break;
				}
			}
			w = getPreviousWidget(w);
		}

		//no widgets here go one level up and search there
		if (w == null && this.getParent() != null && !isKeyTraversalRoot())
		{
			w = this.getParent().getPreviousTraversableWidget(this);
		}
		else if (w == null && isKeyTraversalRoot())
		{
			w = getPreviousTraversableWidget(null);
		}

		return w;
	}

	
	public Dimension getMinContentSize()
	{
		//only use visible widgets
		List<IWidget> widgets = new ArrayList<IWidget>(notifyList.size());

		for (IWidget widget : notifyList)
		{
			if (widget.isVisible())
				widgets.add(widget);
		}

		return this.getLayoutManager().computeMinSize(this, widgets);
	}
	
	
	
	private IWidget activeWidget = null;
	

	
	public void mouseMove(float x, float y) {
	
	
			
		
				IWidget collision = getInternalWidget(x,y);
			//	if (collision != activeButton)
				//{
					if (collision!= null)
						collision.mouseMove(x - collision.getX(), y - collision.getY());
					if (activeWidget != null && activeWidget != collision)
						activeWidget.mouseOff();
					activeWidget = collision;
				//}
				
			
		

		super.mouseMove(x, y);
	}

	
	public float getWorldX()
	{
		return super.getWorldX() + getSpacingAppearance().getLeftMargins();
	}

	
	public float getWorldY()
	{
		return super.getWorldY() + getSpacingAppearance().getBottomMargins();
	}


	
	public void mousePress(boolean pressed, int button, float x, float y) {
	
		super.mousePress(pressed, button, x, y);
		IWidget collision = getInternalWidget(x,y);
		if (collision != null)
			collision.mousePress(pressed,button,x-collision.getX(), y-collision.getY());
		if (collision != activeWidget)
		{
			if (activeWidget != null)
			{
				activeWidget.mousePress(false, button, x-activeWidget.getX(), y-activeWidget.getY());
				activeWidget.mouseOff();
			}
			activeWidget = collision;
		}
	}

	
	public void mouseOff() {
		if (activeWidget != null)
		{
			activeWidget.mouseOff();
		}
		super.mouseOff();
	}

	
    
}
